package org.chen.scene.scheduler;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.chen.common.scene.entity.Attraction;
import org.chen.common.scene.ollama.TourRecommendationResponse;
import org.chen.scene.service.AttractionBatchUpdateService;
import org.chen.scene.service.AttractionService;
import org.chen.scene.service.ollama.TourRecommendationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Component
public class AttractionTourRecommendScheduler {
    @Autowired
    private AttractionService attractionService;

    @Autowired
    private TourRecommendationService tourRecommendationService;

    @Autowired
    private AttractionBatchUpdateService batchUpdateService;

    @Autowired
    private ThreadPoolTaskExecutor attractionRecommendTaskExecutor;

    // 批量处理大小
    private static final int BATCH_SIZE = 50;
    // 每批次间隔时间（毫秒）
    private static final long BATCH_INTERVAL = 2000;

    @Value("${ollama.is_open}")
    private boolean isOpen;

    // 每天21点检查一次未获取建议的景点
    @Scheduled(cron = "0 0 21 * * ?")
    public void fetchAttractionTourRecommend() {
        if (!isOpen){
            log.info("未启用景点建议");
            return;
        }

        log.info("开始获取景点建议");

        try {
            List<Attraction> unfetchedAttractions = attractionService.getUnfetchAttractionTourRecommend();

            if (unfetchedAttractions.isEmpty()) {
                log.info("没有需要获取景点建议的数据");
                return;
            }

            log.info("发现{}个需要获取景点建议的景点", unfetchedAttractions.size());

            // 分批处理
            List<List<Attraction>> batches = partitionList(unfetchedAttractions, BATCH_SIZE);
            log.info("分为{}个批次处理", batches.size());

            // 统计信息
            AtomicInteger successCount = new AtomicInteger(0);
            AtomicInteger failCount = new AtomicInteger(0);
            List<BatchResult> batchResults = new CopyOnWriteArrayList<>();

            // 处理每个批次
            for (int i = 0; i < batches.size(); i++) {
                List<Attraction> batch = batches.get(i);
                log.info("开始处理第{}批次，包含{}个景点", i + 1, batch.size());

                BatchResult batchResult = processBatch(batch, i + 1);
                batchResults.add(batchResult);

                successCount.addAndGet(batchResult.getSuccessCount());
                failCount.addAndGet(batchResult.getFailCount());

                // 批次间暂停，避免对API造成太大压力
                if (i < batches.size() - 1) {
                    try {
                        Thread.sleep(BATCH_INTERVAL);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                        log.warn("批次间隔被中断");
                    }
                }
            }

            // 输出最终统计
            log.info("景点建议获取任务完成 - 成功: {}, 失败: {}, 总计: {}",
                    successCount.get(), failCount.get(), unfetchedAttractions.size());

            // 记录详细批次结果
            logBatchResults(batchResults);

        } catch (Exception e) {
            log.error("获取景点建议任务执行异常", e);
        }
    }

    /**
     * 处理单个批次
     */
    private BatchResult processBatch(List<Attraction> batch, int batchNumber) {
        List<CompletableFuture<ProcessResult>> futures = new ArrayList<>();

        // 异步处理批次中的每个景点
        for (Attraction attraction : batch) {
            CompletableFuture<ProcessResult> future = CompletableFuture
                    .supplyAsync(() -> processAttraction(attraction), attractionRecommendTaskExecutor)
                    .exceptionally(throwable -> {
                        log.error("处理景点{}时发生异常", attraction.getPoiId(), throwable);
                        return new ProcessResult(attraction.getPoiId(), false, throwable.getMessage());
                    });

            futures.add(future);
        }

        // 等待所有任务完成
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(
                futures.toArray(new CompletableFuture[0])
        );

        try {
            allFutures.get(5, TimeUnit.MINUTES);
        } catch (TimeoutException e) {
            log.warn("第{}批次处理超时，部分任务可能未完成", batchNumber);
        } catch (Exception e) {
            log.error("第{}批次处理异常", batchNumber, e);
        }

        // 收集结果
        List<ProcessResult> results = futures.stream()
                .map(future -> {
                    try {
                        return future.get();
                    } catch (Exception e) {
                        log.error("获取处理结果失败", e);
                        return new ProcessResult(null, false, e.getMessage());
                    }
                })
                .collect(Collectors.toList());

        // 批量更新成功的记录
        List<ProcessResult> successResults = results.stream()
                .filter(ProcessResult::isSuccess)
                .collect(Collectors.toList());

        if (!successResults.isEmpty()) {
            try {
                // 调用独立的事务服务
                batchUpdateService.batchUpdateDatabase(successResults);
            } catch (Exception e) {
                log.error("批量更新失败，尝试单个更新", e);
                // 批量更新失败时，回退到单个更新
                batchUpdateService.fallbackToSingleUpdate(successResults);
            }
        }

        BatchResult batchResult = new BatchResult(batchNumber,
                successResults.size(),
                results.size() - successResults.size(),
                results);

        log.info("第{}批次完成 - 成功: {}, 失败: {}",
                batchNumber, batchResult.getSuccessCount(), batchResult.getFailCount());

        return batchResult;
    }

    /**
     * 处理单个景点
     */
    private ProcessResult processAttraction(Attraction attraction) {
        try {
            log.debug("开始处理景点: {}", attraction.getPoiId());

            TourRecommendationResponse recommendation = tourRecommendationService
                    .getRecommendation(attraction.getDistrictName());

            if (recommendation != null && recommendation.getRecommend_Hours() != null) {
                return new ProcessResult(attraction.getPoiId(), true, null,
                        recommendation.getRecommend_Hours());
            } else {
                return new ProcessResult(attraction.getPoiId(), false, "获取到空的推荐结果");
            }

        } catch (Exception e) {
            log.error("处理景点{}失败", attraction.getPoiId(), e);
            return new ProcessResult(attraction.getPoiId(), false, e.getMessage());
        }
    }

    /**
     * 分割列表
     */
    private <T> List<List<T>> partitionList(List<T> list, int batchSize) {
        List<List<T>> partitions = new ArrayList<>();
        for (int i = 0; i < list.size(); i += batchSize) {
            partitions.add(list.subList(i, Math.min(i + batchSize, list.size())));
        }
        return partitions;
    }

    /**
     * 记录批次结果
     */
    private void logBatchResults(List<BatchResult> batchResults) {
        log.info("=== 批次处理详细结果 ===");
        for (BatchResult result : batchResults) {
            log.info("批次{}: 成功{}, 失败{}",
                    result.getBatchNumber(),
                    result.getSuccessCount(),
                    result.getFailCount());
        }
        log.info("========================");
    }

    // 内部类：处理结果
    @Data
    @AllArgsConstructor
    public static class ProcessResult {
        private Long poiId;
        private boolean success;
        private String errorMessage;
        private String recommendHours;

        public ProcessResult(Long poiId, boolean success, String errorMessage) {
            this(poiId, success, errorMessage, null);
        }
    }

    // 内部类：批次结果
    @Data
    @AllArgsConstructor
    public static class BatchResult {
        private int batchNumber;
        private int successCount;
        private int failCount;
        private List<ProcessResult> results;
    }
}
